; Measure vertical retrace time
TestCycPerFrame	proc

		; Set up the Measurer
		; (a bunch of 'inc de' instructions)

N_INCS		equ	4287	; Max ~80000 T-states between interrupts

		ld	hl,Measurer
		ld	de,Measurer+1
		ld	bc,N_INCS-1

		ld	(hl),19		; inc de
					; inc de is 7T-states (it's the core of this algorithm)
		ldir			; This takes more than 1 frame

		; Wait for first retrace (so we aren't caught in an inconvenient place)
		; Also give the BIOS a chance to check the keyboard.
		halt

		inc	hl
		ld	(hl),243	; di
		inc	hl
		ld	(hl),62		; ld a,N
		inc	hl
		ld	(hl),1		; Error code 1: "Too slow"
		inc	hl
		ld	(hl),195	; jp NN
		inc	hl
		ld	(hl),LOW Finish
		inc	hl
		ld	(hl),HIGH Finish

		; Clear cycles per frame counter
		xor	a
		ld	h,a
		ld	l,a
		ld	(CycFrm1),hl
		ld	(CycFrm3),a

		; Prepare everything for IM2
		ld	hl,IntMeasure
		ld	(IntVec),hl

		; The test works on this principle. Say you have two gears, a small one with 7
		; teeth and a bigger one with an unknown number of teeth, but you can check at
		; any time whether the big gear has completed a revolution. Now, you can count
		; how many revs of the small gear it takes for the big gear to complete a
		; whole turn, but that will only be exact if the number of teeth in the big
		; gear is an exact multiple of 7. But if you wait until the big gear takes 7
		; revs, you can be sure that (a) the gears are in the same relative position
		; as at the start, and (b) more importantly, the number of revs of the small
		; gear is exactly the number of teeth in the big gear.

		; Run the test 7 times.
		ld	b,1+7		; Number of times to run. The first one is used to sync.
		ld	de,0		; Do not add anything the first time

		; Switch to IM 2
		im	2

		; Start of measurement loop
		halt
		; never returns

		endp


IntMeasure	proc
		local	Waiting, Exit, TooFast

		; On arrival:		; 30T ; (19 + 11)
		in	a,(99h)		; 12T ; Ack interrupt
		inc	sp		; 7T
		inc	sp		; 7T  ; Ignore return address
		ld	hl,(CycFrm1)	; 17T
		add	hl,de		; 12T
		ld	(CycFrm1),hl	; 17T
		ld	hl,CycFrm3	; 11T
		ld	a,(hl)		; 8T
		adc	a,0		; 8T
		ld	(hl),a		; 8T
		ld	hl,TooFast	; 11T
		ld	(IntVec),hl	; 17T
		dec	b		; 5T  ; Check test counter
		jp	z,Exit		; 11T
		ld	hl,0		; 11T ; delay
		cp	0		; 8T  ; delay, total 19T
		ei			; 5T

		; Wait for a while more. This reduces memory requirements by reducing the number
		; of INC DE instructions expected.
		ld	hl,1658		; 11T ; adjusted to give 50,001T = 7*7143 total
					; 1658 * (
Waiting:	dec	hl		; 7T
		ld	a,h		; 5T
		or	l		; 5T
		jr	nz,Waiting	; 13T
					; )
					; -5T ; for the false jr condition

		;  30+12+7+7+17+12+17+11+8+8+8+11+17+5+11+11+8+5+11+1658*(7+5+5+13)-5+11+11+17+11 = 7*7143

		ld	de,7143		; 11T ; Preload DE with total cycles in this routine / 7
					;       (the value it would have if INC DE was executing
					;        continuously this whole time)
		ld	hl,IntMeasure	; 11T
		ld	(IntVec),hl	; 17T ; hopefully we're not so unlucky as to get an interrupt right here!
		jp	Measurer	; 11T ; ... or here

Exit:
		; We're done. There's almost 1 frame left until the next expected interrupt.
		; Total so far:		; 181T = 30+12+7+7+17+12+17+11+8+8+8+11+17+5+11
		im	1		; 10T
		ei			; 5T
		ret			; 11T


; Triggered if an interrupt comes before ~50K T-states
TooFast:	ld	a,2		; Error code 2: "Unexpected or too fast interrupt"
		jp	Finish

		endp
